﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Kugar.Core.ExtMethod;
using Kugar.Core.Network.Web;
using Kugar.Core.PluginFramework.Common;
using Newtonsoft.Json.Linq;

namespace Kugar.Core.PluginFramework.Host
{
    /// <summary>
    /// 插件宿主
    /// </summary>
    public class PluginHost:IEnumerable<IPlugin>
    {
        private Dictionary<string,IPlugin> _cachePlugins=new Dictionary<string, IPlugin>(StringComparer.CurrentCultureIgnoreCase);
        private ReaderWriterLockSlim _locker=new ReaderWriterLockSlim();
        private static Dictionary<Type,IPlugin> _cacheSharedInstancePlugin=new Dictionary<Type, IPlugin>();
        private string _pluginLoadPath = "";
        private RequestThreadLocal<IPlugin> _currentPlugin = new RequestThreadLocal<IPlugin>(); //存放当前线程调用的插件

        static PluginHost()
        {
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

        }


        ///// <summary>
        ///// 构造函数,默认加载所有类型的插件
        ///// </summary>
        //public PluginHost():this(typeof(PluginBase))
        //{

        //}

        /// <summary>
        /// 构造函数,只加载指定基类的所有插件,可以用于分别加载不同用途的插件
        /// </summary>
        /// <param name="baseType">指定要加载的插件的基类,默认为null,.则为PluginBase作为基类</param>
        /// <param name="throwLoadError">当加载插件出错的时候,是否抛出错误,并终止加载的操作</param>
        /// <param name="pluginLoadPath">插件加载的目录,如果该目录不为空,则会搜索目录下所有dll,并加载dll到当前应用程序域内,默认为当前目录<br/>
        /// 如 Plugins/插件名xxxx 这样的结构,如需要多级目录,请使用RegisterPlugin执行加载
        /// </param>
        public PluginHost(Type baseType=null,bool throwLoadError=false,string pluginLoadPath="")
        {
            //var baseType = typeof(PluginBase);

            if (baseType==null)
            {
                baseType= typeof(PluginBase);
            }

            _pluginLoadPath = pluginLoadPath;

            if (!string.IsNullOrWhiteSpace(_pluginLoadPath))
            {
                var files=Directory.GetFiles(_pluginLoadPath, "*.dll", SearchOption.AllDirectories);

                //files=new string[]
                //      {
                //          @"Plugins\Kugar.Core.PluginFramework.PluginTest.MongoDBPlugin\Kugar.Core.PluginFramework.PluginTest.MongoDBPlugin.dll"
                //      };

                foreach (var file in files.OrderBy(x=>x))
                {
                    try
                    {
                        var tmpAssembly=Assembly.LoadFrom(file);
                        
                        //if (tmpAssembly.GetExportedTypes().Any(x => IsMatchType(x, baseType)))
                       // {
                        //    Assembly.LoadFrom(file);
                        //}
                    }
                    catch (Exception e)
                    {
                        AssemblyResolveError?.Invoke(file,e);
                    }
                }
            }

            var emptyTypes = new Type[0];

            var pluginTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x =>
                {
                    try
                    {
                        return x.GetTypes();
                    }
                    catch (Exception e)
                    {
                        return emptyTypes;
                    }
                    
                })
                .Where(x=>x!=null)
                .Where(x => IsMatchType(x,baseType));

            foreach (var pluginType in pluginTypes)
            {
                if (!pluginType.IsSubclassOf(baseType))
                {
                    continue;
                }

                try
                {
                    var shareAttr = pluginType.GetAttribute<SharedInstanceAttribute>();

                    IPlugin plugin = null;

                    if (shareAttr!=null)
                    {
                        lock (_cacheSharedInstancePlugin)
                        {
                            if (!_cacheSharedInstancePlugin.TryGetValue(pluginType,out plugin))
                            {
                                plugin = (IPlugin)Activator.CreateInstance(pluginType);

                                _cacheSharedInstancePlugin.Add(pluginType,plugin);
                            }
                        }
                    }
                    else
                    {
                        plugin = (IPlugin)Activator.CreateInstance(pluginType);
                    }
                    
                    _cachePlugins.Add(plugin.PluginName, plugin);
                }
                catch (Exception e)
                {
                    if (throwLoadError)
                    {
                        throw;
                    }
                    else
                    {
                        PluginLoadError?.Invoke(pluginType, e);
                    }
                    
                }
            }
        }
        
        /// <summary>
        /// 执行一个指定名称的插件的指定名称函数
        /// </summary>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public object Execute(string pluginName, string funcName, JObject args)
        {
            var plugin = getPluginByName(pluginName);

            if (plugin != null)
            {
                Exception error = null;

                try
                {
                    var result = plugin.OnExecuteCoreJson(funcName, args);

                    onAfterCallFunction(pluginName, plugin, funcName, args, null);

                    return result;
                }
                catch (Exception e)
                {
                    onAfterCallFunction(pluginName, plugin, funcName, args, e);
                    throw;
                }
            }
            else
            {
                throw new MissingPluginException(pluginName);
            }
        }

        /// <summary>
        /// 执行一个指定名称的插件的指定名称函数
        /// </summary>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public T ExecuteByJson<T>(string pluginName, string funcName, JObject args)
        {
            return (T) Execute(pluginName, funcName, args);
        }

        /// <summary>
        /// 使用异步的方式执行一个函数,如果该函数为async标识的函数,则直接执行,如果该函数非异步函数,则使用同步方法调用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public async Task<T> ExecuteAsync<T>(string pluginName, string funcName, JObject args)
        {
            var result = Execute(pluginName, funcName, args);

            if (result is Task<T>)
            {
                return await(Task<T>)result;
            }
            else
            {
                return (T)result;
            }
        }

        /// <summary>
        /// 使用异步的方式执行一个函数,如果该函数为async标识的函数,则直接执行,如果该函数非异步函数,则使用同步方法调用 <br/>
        /// 忽略异步函数返回值
        /// </summary>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public async Task ExecuteAsync(string pluginName, string funcName, JObject args)
        {
            var result = Execute(pluginName, funcName, args);

            if (result is Task)
            {
                await (Task)result;
            }
            else
            {
                //return (T)result;
            }
        }

        /// <summary>
        /// 执行一个指定名称的插件的指定名称函数
        /// </summary>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public object Execute(string pluginName, string funcName, IReadOnlyDictionary<string, object> args)
        {
            var plugin = getPluginByName(pluginName);

            if (plugin!=null)
            {
                Exception error = null;

                try
                {
                    var result = plugin.OnExecuteCore(funcName, args);

                    onAfterCallFunction(pluginName, plugin, funcName, args, null);

                    return result;
                }
                catch (Exception e)
                {
                    onAfterCallFunction(pluginName, plugin, funcName, args, e);
                    throw;
                }
            }
            else
            {
                throw new MissingPluginException(pluginName);
            }
        }

        /// <summary>
        /// 执行一个指定名称的插件的指定名称函数
        /// </summary>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public T Execute<T>(string pluginName, string funcName, IReadOnlyDictionary<string, object> args)
        {
            return (T)Execute(pluginName, funcName, args);
        }

        /// <summary>
        /// 使用异步的方式执行一个函数,如果该函数为async标识的函数,则直接执行,如果该函数非异步函数,则使用同步方法调用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public async Task<T> ExecuteAsync<T>(string pluginName, string funcName, IReadOnlyDictionary<string, object> args)
        {
            var result = Execute(pluginName, funcName, args);

            if (result is Task<T>)
            {
                return await (Task<T>)result;
            }
            else
            {
                return (T)result;
            }
        }

        /// <summary>
        /// 使用异步的方式执行一个函数,如果该函数为async标识的函数,则直接执行,如果该函数非异步函数,则使用同步方法调用 <br/>
        /// 忽略异步函数返回值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pluginName"></param>
        /// <param name="funcName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public async Task ExecuteAsync(string pluginName, string funcName, IReadOnlyDictionary<string, object> args)
        {
            var result = Execute(pluginName, funcName, args);

            if (result is Task)
            {
                await (Task)result;
            }
            else
            {
                //return (T)result;
            }
        }

        /// <summary>
        /// 注册一个插件到容器中,并使用指定名称
        /// </summary>
        /// <param name="pluginName"></param>
        /// <param name="plugin"></param>
        public void RegisterPlugin(string pluginName, IPlugin plugin)
        {
            _locker.EnterWriteLock();

            try
            {
                if (_cachePlugins.ContainsKey(pluginName))
                {
                    throw new ArgumentOutOfRangeException(nameof(pluginName), "不允许重复的插件名称");
                }

                _cachePlugins.Add(pluginName, plugin);
            }
            catch (Exception e)
            {
                throw;
            }
            finally
            {
                _locker.ExitWriteLock();
            }


        }

        /// <summary>
        /// 注册一个插件到容器中
        /// </summary>
        /// <param name="plugin"></param>
        public void RegisterPlugin(IPlugin plugin)
        {
            this.RegisterPlugin(plugin.PluginName,plugin);
        }

        /// <summary>
        /// 判断当前是否包含指定名称的插件,忽略大小写
        /// </summary>
        /// <param name="pluginName"></param>
        /// <returns></returns>
        public bool ContainsPlugin(string pluginName)
        {
            _locker.EnterReadLock();

            try
            {
                return _cachePlugins.ContainsKey(pluginName);
            }
            catch (Exception e)
            {
                return false;
            }
            finally
            {
                _locker.ExitReadLock();
            }
        }

        /// <summary>
        /// 获取指定名称的插件
        /// </summary>
        /// <param name="pluginName">插件名称</param>
        /// <returns>如果插件不存在,则返回null</returns>
        public IPlugin GetPlugin(string pluginName)
        {
            IPlugin plugin = null;

            if (_cachePlugins.TryGetValue(pluginName,out plugin))
            {
                return plugin;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取指定名称的插件
        /// </summary>
        /// <param name="pluginName">插件名称</param>
        /// <returns>如果插件不存在,则返回null</returns>
        public T GetPlugin<T>(string pluginName) where T : IPlugin
        {
            IPlugin plugin = null;

            if (_cachePlugins.TryGetValue(pluginName, out plugin))
            {
                return (T)plugin;
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 注销当前宿主
        /// </summary>
        public void Close()
        {
            _locker.EnterWriteLock();

            try
            {
                foreach (var plugin in _cachePlugins)
                {
                    plugin.Value.Dispose();
                }

                _cachePlugins.Clear();

            }
            catch (Exception e)
            {
                throw;
            }
            finally
            {
                _locker.ExitWriteLock();
            }


        }

        /// <summary>
        /// 当前宿主的插件总数
        /// </summary>
        public int Count => _cachePlugins.Count;

        public string PluginLoadPath => _pluginLoadPath;

        public IPlugin this[string pluginName]
        {
            get => _cachePlugins.TryGetValue(pluginName);
        }

        /// <summary>
        /// 当加载插件出错时,触发该事件
        /// </summary>
        public event PluginLoadError PluginLoadError;

        /// <summary>
        /// 当指定插件函数出错时,触发该事件
        /// </summary>
        public event PluginExecuteError PluginExecuteError;

        /// <summary>
        /// 当触发插件函数正常时,触发该事件
        /// </summary>
        public event PluginExecuted PluginExecuted;

        /// <summary>
        /// 应用程序集加载失时,触发该事件
        /// </summary>
        public event PluginAssemblyError AssemblyResolveError;

        private void onAfterCallFunction(string pluginName, IPlugin plugin, string funcName,
            JObject args,
            Exception error)
        {
            if (error != null)
            {
                if (PluginExecuteError != null)
                {
                    PluginExecuteError?.Invoke(pluginName, plugin, funcName, new JObjectToDictionary(args), error);
                }

            }
            else
            {
                if (PluginExecuted != null)
                {
                    PluginExecuted(pluginName, plugin, funcName, new JObjectToDictionary(args));
                }

            }
        }

        private void onAfterCallFunction(string pluginName, IPlugin plugin, string funcName,
            IEnumerable<KeyValuePair<string, object>> args,
            Exception error)
        {
            if (error != null)
            {
                if (PluginExecuteError != null)
                {
                    PluginExecuteError(pluginName, plugin, funcName, args, error);
                }

            }
            else
            {
                if (PluginExecuted != null)
                {
                    PluginExecuted(pluginName, plugin, funcName, args);
                }
            }
        }

        private IPlugin getPluginByName(string name)
        {
            _locker.EnterReadLock();

            try
            {
                if (_cachePlugins.TryGetValue(name, out var plugin))
                {
                    return plugin;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception e)
            {
                throw;
            }
            finally
            {
                _locker.ExitReadLock();
            }

            
        }

        private bool IsMatchType(Type checkType,Type baseType)
        {
            return checkType.IsClass && //只加载class
                   checkType.IsImplementlInterface(typeof(IPlugin)) && //必须为实现IPlugin接口
                   checkType != baseType && //避免加载到自定义基类 
                   checkType != typeof(PluginBase) && //避免加载到基类 
                   !checkType.IsAbstract &
                   checkType.GetAttribute<IgoneExportAttribute>() == null; //忽略标识为Igone的类

        }

        private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            var assembly= AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x =>
            {
                var asname=new AssemblyName(args.Name);
                return x.FullName == args.Name || x.GetName().Name == asname.Name;
            });

            if (assembly==null && args.RequestingAssembly!=null)
            {
                var tmp=new AssemblyName(args.Name);

                var filepath = Path.Combine(Path.GetDirectoryName(args.RequestingAssembly.Location), tmp.Name + ".dll");

                if (File.Exists(filepath))
                {
                    assembly = Assembly.LoadFrom(filepath);
                }
                
            }

            return assembly;

            //throw new NotImplementedException();
        }

        public IEnumerator<IPlugin> GetEnumerator()
        {
            _locker.EnterReadLock();
            
            foreach (var plugin in _cachePlugins)
            {
                yield return plugin.Value;
            }

            _locker.ExitReadLock();
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    

    public class MissingPluginException : Exception
    {
        public MissingPluginException(string pluginName)
        {
            PluginName = pluginName;
        }

        public string PluginName { get; private set; }
    }

    public delegate void PluginLoadError(Type pluginType, Exception loadingError);

    public delegate void PluginExecuteError(string pluginName, IPlugin plugin, string funcName, IEnumerable<KeyValuePair<string, object>> args,
        Exception error);

    public delegate void PluginExecuted(string pluginName, IPlugin plugin, string funcName, IEnumerable<KeyValuePair<string, object>> argss);

    public delegate void PluginAssemblyError(string filePath, Exception error);
}
